﻿using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Reflection.Emit;
using Framework.Common.Reflection.RunSharpEmit;
using System.Collections;

namespace System.Linq
{
    public static class IEnumerableExtension
    {
        #region 安全的变成为字典
        /// <summary>
        /// 安全的变成为字典
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="source"></param>
        /// <param name="keySelector"></param>
        /// <param name="elementSelector"></param>
        /// <returns></returns>
        public static Dictionary<TKey, TElement> ToDictionarySafe<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
        {
            return source
                .GroupBy(keySelector)
                .Select(p => p.First())
                .ToDictionary(keySelector, elementSelector);
        }
        /// <summary>
        ///  转为hashset
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
        {
            if (source != null)
            {
                return new HashSet<T>(source);
            }
            return new HashSet<T>();
        }
        #endregion

        #region each extension
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumberable"></param>
        /// <param name="action"></param>
        public static void Each<T>(this IEnumerable<T> enumberable, Action<T> action)
        {
            if (enumberable != null)
            {
                foreach (var item in enumberable)
                {
                    action(item);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="enumberable"></param>
        /// <param name="func"></param>
        public static IEnumerable<TResult> Each<T, TResult>(this IEnumerable<T> enumberable, Func<T, TResult> func)
        {
            if (enumberable != null)
            {
                foreach (var item in enumberable)
                {
                    yield return func(item);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="enumberable"></param>
        /// <param name="func"></param>
        /// <param name="filter"></param>
        public static IEnumerable<TResult> Each<T, TResult>(this IEnumerable<T> enumberable, Func<T, TResult> func, Func<TResult, bool> filter)
        {
            if (enumberable != null)
            {
                foreach (var item in enumberable)
                {
                    TResult result = func(item);
                    if (filter(result))
                    {
                        yield return result;
                    }
                }
            }
        }
        #endregion

        #region safe count
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ienum"></param>
        /// <returns></returns>
        public static int SafeCount<T>(this IEnumerable<T> ienum)
        {
            if (ienum != null)
            {
                return ienum.Count();
            }
            return 0;
        }

        #endregion

        #region Safe ToList
        /// <summary>
        /// 安全转化为List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static List<T> ToListSafe<T>(this IEnumerable<T> source)
        {
            if (source != null)
            {
                return source.ToList();
            }
            return new List<T>();
        }
        #endregion

        #region 匿名类型转换成已知类型实体
        #region 队列转换
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="ienums"></param>
        /// <returns></returns>
        public static IEnumerable<TTarget> CastToIEnumerable<TTarget>(this IEnumerable ienums)
        {
            var enumerator = ienums.GetEnumerator();
            if (enumerator.MoveNext())
            {
                object obj = enumerator.Current;
                Func<object, TTarget> func = GetDelegate<TTarget>(obj.GetType());
                yield return func(obj);
                while (enumerator.MoveNext())
                {
                    yield return func(enumerator.Current);
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="ienums"></param>
        /// <returns></returns>
        public static List<TTarget> CastToList<TTarget>(this IEnumerable ienums)
        {
            return ienums.CastToIEnumerable<TTarget>().ToList();
        }
        #endregion

        #region 单个对象转换
        /// <summary>
        /// cast anonymous object to TTarget
        /// </summary>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static TTarget CastTo<TTarget>(this object obj)
        {
            if (obj == null) return default(TTarget);
            Func<object, TTarget> func = GetDelegate<TTarget>(obj.GetType());
            return func(obj);
        }
        #endregion

        #region 核心方法
        const string PreGet = "get_";
        const string PreSet = "set_";

        /// <summary>
        /// get dynamic method object
        /// </summary>
        /// <param name="paramType"></param>
        /// <param name="sourceType"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        private static Delegate GetDynamicMethod<TFunc>(Type paramType, Type sourceType, Type targetType)
        {
            string key = paramType.GetHashCode().ToString() + sourceType.GetHashCode().ToString() + targetType.GetHashCode().ToString();

            object result;
            if (InterfaceInstanceDict.TryGetValue(key, out result))
            {
                return (Delegate)result;
            }
            else
            {
                var names = from pa in sourceType.GetProperties()
                            join pb in targetType.GetProperties()
                            on pa.Name equals pb.Name
                            select new { pa.Name, SourcePropertyType = pa.PropertyType, TargetPropertyType = pb.PropertyType };
                #region emit
                DynamicMethodGen dmg = DynamicMethodGen.Static(targetType).Method(targetType).Parameter(paramType, "s");
                CodeGen cg = dmg.GetCode();
                {
                    Operand op = cg.Local(Exp.New(targetType));
                    Operand param = paramType == sourceType ? cg.Arg("s") : cg.Arg("s").Cast(sourceType);
                    foreach (var obj in names)
                    {
                        //if the source property type equals target type
                        if (obj.SourcePropertyType != obj.TargetPropertyType)
                        {
                            Operand nullableValue = cg.Local(param.Invoke(string.Concat(PreGet, obj.Name)));

                            if (obj.SourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                            {
                                cg.If(nullableValue.Invoke(string.Concat(PreGet, "HasValue")).EQ(true));
                                cg.Invoke(op, string.Concat(PreSet, obj.Name), nullableValue.Cast(obj.TargetPropertyType));
                                cg.End();
                            }
                            else
                            {
                                cg.Invoke(op, string.Concat(PreSet, obj.Name), nullableValue.Cast(obj.TargetPropertyType));
                            }
                        }
                        else
                        {
                            cg.Invoke(op, string.Concat(PreSet, obj.Name), param.Invoke(string.Concat(PreGet, obj.Name)));
                        }
                    }
                    cg.Return(op);
                }
                #endregion
                DynamicMethod dm = dmg.GetCompletedDynamicMethod(true);
                var tmpFunc = dm.CreateDelegate(typeof(TFunc));
                InterfaceInstanceDict.TryAdd(key, tmpFunc);
                return tmpFunc;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="sourceType"></param>
        /// <returns></returns>
        private static Func<object, TTarget> GetDelegate<TTarget>(Type sourceType)
        {
            Type paramType = typeof(object);
            Type targetType = typeof(TTarget);
            return (Func<object, TTarget>)GetDynamicMethod<Func<object, TTarget>>(paramType, sourceType, targetType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <returns></returns>
        private static Func<TSource, TTarget> GetDelegate<TSource, TTarget>()
        {
            Type sourceType = typeof(TSource);
            Type targetType = typeof(TTarget);
            return (Func<TSource, TTarget>)GetDynamicMethod<Func<TSource, TTarget>>(sourceType, sourceType, targetType);
        }
        #endregion

        static readonly ConcurrentDictionary<string, object> InterfaceInstanceDict;

        /// <summary>
        /// static constructor
        /// </summary>
        static IEnumerableExtension()
        {
            InterfaceInstanceDict = new ConcurrentDictionary<string, object>();
        }
        #endregion

        #region 判断是否为空

        /// <summary>
        /// IsNullOrEmpty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumberable"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumberable)
        {
            return enumberable == null || !enumberable.Any();
        }

        /// <summary>
        /// IsNotNullOrEmpty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumberable"></param>
        /// <returns></returns>
        public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> enumberable)
        {
            return enumberable != null && enumberable.Any();
        }


        /// <summary>
        /// IsNullOrEmpty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static bool IsNullOrEmpty<T>(this IList<T> list)
        {
            return list == null || list.Count == 0;
        }

        /// <summary>
        /// IsNotNullOrEmpty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static bool IsNotNullOrEmpty<T>(this IList<T> list)
        {
            return list != null && list.Count > 0;
        }

        #endregion
    }
}
